/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright Blazebit
 */

package com.blazebit.persistence.view.impl.collection;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Christian Beikov
 * @since 1.2.0
 */
public final class RecordingUtils {

    private RecordingUtils() {
    }

    public static Collection<Object> compensateObjects(Collection<?> elements, Collection<Object> objectsToCompensate) {
        List<Object> newObjectsToRemove = null;
        // Initialize the new collection if we found an overlap
        for (Object o : objectsToCompensate) {
            if (elements.contains(o)) {
                newObjectsToRemove = new ArrayList<>(objectsToCompensate.size());
                break;
            }
        }
        // If not initialized, there's no overlap
        if (newObjectsToRemove == null) {
            return objectsToCompensate;
        }

        // Only add non-elided objects
        for (Object o : objectsToCompensate) {
            if (!elements.remove(o)) {
                newObjectsToRemove.add(o);
            }
        }

        return newObjectsToRemove;
    }

    public static List<Object> replaceElements(Collection<?> elements, Object oldElem, Object elem) {
        List<Object> newElements = null;

        int i = 0;
        for (Object element : elements) {
            if (element == oldElem) {
                if (newElements == null) {
                    newElements = new ArrayList<>(elements);
                }
                newElements.set(i, elem);
            }
            i++;
        }

        return newElements;
    }

    public static Map<Object, Object> replaceElements(Map<?, ?> elements, Object oldKey, Object oldValue, Object newKey, Object newValue) {
        Map<Object, Object> newElements = null;
        Iterator<? extends Map.Entry<?, ?>> iter = elements.entrySet().iterator();

        while (iter.hasNext()) {
            Map.Entry<?, ?> entry = iter.next();
            if (entry.getKey() == oldKey || entry.getValue() == oldValue) {
                if (newElements == null) {
                    newElements = new LinkedHashMap<>(elements);
                }
                newElements.remove(oldKey);
                newElements.put(newKey, newValue);
            }
        }

        return newElements;
    }

    public static List<Map.Entry<Object, Object>> replaceEntries(Collection<? extends Map.Entry<?, ?>> elements, Object oldKey, Object oldValue, Object newKey, Object newValue) {
        List<Map.Entry<Object, Object>> newElements = null;

        int i = 0;
        for (Map.Entry<?, ?> element : elements) {
            if (element.getKey() == oldKey || element.getValue() == oldValue) {
                if (newElements == null) {
                    newElements = new ArrayList<>((Collection<? extends Map.Entry<Object, Object>>) elements);
                }
                newElements.set(i, new AbstractMap.SimpleEntry<>(newKey, newValue));
            }
            i++;
        }

        return newElements;
    }

    public static List<Object> replaceElements(Collection<?> elements, Map<Object, Object> objectMapping) {
        List<Object> newElements = null;

        if (objectMapping != null) {
            int i = 0;
            for (Object element : elements) {
                Object newElement = objectMapping.get(element);
                if (newElement != null) {
                    if (newElements == null) {
                        newElements = new ArrayList<>(elements);
                    }
                    newElements.set(i, newElement);
                }
                i++;
            }
        }

        return newElements;
    }

    public static Map<Object, Object> replaceElements(Map<?, ?> elements, Map<Object, Object> objectMapping) {
        Map<Object, Object> newElements = null;
        Iterator<? extends Map.Entry<?, ?>> iter = elements.entrySet().iterator();

        while (iter.hasNext()) {
            Map.Entry<?, ?> entry = iter.next();
            Object newKey = objectMapping.get(entry.getKey());
            Object newValue = objectMapping.get(entry.getValue());
            if (newKey != null || newValue != null) {
                if (newElements == null) {
                    newElements = new LinkedHashMap<>(elements);
                }
                if (newKey == null) {
                    newKey = entry.getKey();
                } else if (newValue == null) {
                    newValue = entry.getValue();
                }
                newElements.remove(entry.getKey());
                newElements.put(newKey, newValue);
            }
        }

        return newElements;
    }

    public static List<Map.Entry<Object, Object>> replaceEntries(Collection<? extends Map.Entry<?, ?>> elements, Map<Object, Object> objectMapping) {
        List<Map.Entry<Object, Object>> newElements = null;

        int i = 0;
        for (Map.Entry<?, ?> element : elements) {
            Object newKey = objectMapping.get(element.getKey());
            Object newValue = objectMapping.get(element.getValue());
            if (newKey != null || newValue != null) {
                if (newElements == null) {
                    newElements = new ArrayList<>((Collection<? extends Map.Entry<Object, Object>>) elements);
                }
                if (newKey == null) {
                    newKey = element.getKey();
                } else if (newValue == null) {
                    newValue = element.getValue();
                }
                newElements.set(i, new AbstractMap.SimpleEntry<>(newKey, newValue));
            }
            i++;
        }

        return newElements;
    }
}
